import { isProd, time2Millis } from "./cmm.util";
import { cloneDeep } from "lodash";
import { timer } from "rxjs";
import { logger } from "../configs/log.config";

/**
 * 一个简易的缓存
 * 注意：存在定时gc，需要手动 destroy 释放内存
 */
export class AppCache {
  // 缓存内容
  private __cache: Map<any, any> = new Map();
  // 缓存过期时间
  private __expire: Map<any, number> = new Map();

  /**
   * 构造函数
   * 1. 默认过期时间无限期
   * 2. 默认垃圾回收时间 1 小时
   * @param __expireTime 默认过期时间
   * @param __gcTime 默认垃圾回收时间
   * @param __isDeepClone 是否深度克隆
   */
  constructor(
    private readonly __expireTime: string | number = -1,
    private readonly __gcTime: string | number = "1h",
    private readonly __isDeepClone: boolean = false,
  ) {
    this.__expireTime = time2Millis(this.__expireTime);
    this.__gcTime = time2Millis(this.__gcTime);
    this.__gc();
    AppCache.activeCount++;
  }

  private static activeCount = 0;

  static getActiveCount() {
    return AppCache.activeCount;
  }

  /**
   * 回收一个 key
   * @param key key
   * @private
   */
  private __gcKey(key: any) {
    if (!this.__isExpired(key)) return;
    this.remove(key);
  }

  // gc 定时器
  private __gcTimer = setTimeout(() => {});
  /**
   * 定时垃圾回收
   * @private
   */
  private __gc() {
    clearTimeout(this.__gcTimer);
    for (const [key, value] of this.__cache.entries()) {
      this.__gcKey(key);
    }
    this.__gcTimer = setTimeout(this.__gc.bind(this), this.__gcTime as number);
  }

  /**
   * 获取过期时间 （-1 代表永久有效）
   * 1. 如果没有设置过期时间，则返回-1
   * @param key
   * @private
   */
  private __getExpireTime(key: any) {
    return this.__expire.get(key) ?? -1;
  }

  /**
   * 是否过期
   * 1. -1 代表永久有效
   * @param key 键
   * @private
   */
  private __isExpired(key: any) {
    if (this.__getExpireTime(key) === -1) return false;
    return this.__getExpireTime(key) < new Date().getTime();
  }

  /**
   * 设置过期时间
   * @param key 键
   * @param expire 过期时间
   * @private
   */
  private __setExpire(key: any, expire: string | number) {
    const t = time2Millis(expire);
    if (t == -1) return;
    this.__expire.set(key, t + new Date().getTime());
  }

  /**
   * 获取缓存
   * @param key 键
   * @param def 默认值（不存在时返回）
   */
  get(key: any, def?: any): any {
    if (this.__isExpired(key)) {
      this.remove(key);
      return this.__cloneIf(def);
    }
    return this.__cloneIf(this.__cache.get(key) ?? def);
  }

  /**
   * 判断是否需要克隆并返回内容
   * @param value 值
   * @private
   */
  private __cloneIf(value: any) {
    return this.__isDeepClone ? cloneDeep(value) : value;
  }

  /**
   * 是否存在
   * @param key 键
   */
  has(key: any): boolean {
    if (this.__isExpired(key)) {
      this.remove(key);
      return false;
    }
    return this.__cache.has(key);
  }

  /**
   * 设置缓存
   * @param key 键
   * @param value 值
   * @param expire 过期时间
   */
  set(key: any, value: any, expire: string | number = this.__expireTime): void {
    this.__cache.set(key, value);
    this.__setExpire(key, expire);
  }

  /**
   * 获取并设置
   * 1. 如果存在则返回原值
   * 2. 如果不存在则设置值，并返回设置值
   * @param key   键
   * @param value 值
   * @param expire 过期时间
   */
  getSet(key: any, value: any, expire: string | number = -1): any {
    const v = this.get(key);
    if (v === undefined) {
      this.set(key, value, expire);
      return value;
    }
    return v;
  }

  /**
   * 移除
   * 1. 移除缓存
   * 2. 移除过期时间
   * @param key key
   */
  remove(key: any): void {
    this.__cache.delete(key);
    this.__expire.delete(key);
  }

  /**
   * 清空
   */
  clear() {
    this.__cache.clear();
    this.__expire.clear();
  }

  size(): number {
    this.__gc();
    return this.__cache.size;
  }

  /**
   * 销毁
   */
  destroy() {
    this.clear();
    clearTimeout(this.__gcTimer);
    AppCache.activeCount--;
  }
}

timer(1000).subscribe(() => {
  setInterval(() => {
    if (isProd() || 1) return false;
    logger.info("cache active count: %s", AppCache.getActiveCount());
  }, 3000);
});
